package com.zmj.dubbo.proxy;

import com.zmj.dubbo.bean.RpcRequest;
import com.zmj.dubbo.handler.RpcProxyHandler;
import com.zmj.dubbo.registry.IServiceDiscovery;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


/**
 * RPC客户端 Proxy
 * @author zhangmj
 * @date 2018/9/22 00:41
 */
@Slf4j
@Component
public class RPCClientProxy {

    private IServiceDiscovery serviceDiscovery ;

    public RPCClientProxy(IServiceDiscovery serviceDiscovery) {
        this.serviceDiscovery = serviceDiscovery;
    }

    public <T> T create(final Class<T> interfaceClass){
        return (T)Proxy.newProxyInstance(interfaceClass.getClassLoader(),
                new Class<?>[]{interfaceClass}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 代理对象要对的事情是什么？ 网络通信 Netty 数据的交互  Socket.out("我要访问某某类、方法等")
                        RpcRequest rpcRequest = new RpcRequest();
                        rpcRequest.setClassName(method.getDeclaringClass().getName());
                        rpcRequest.setMethodName(method.getName());
                        rpcRequest.setTypes(method.getParameterTypes());
                        rpcRequest.setParams(args);

                        // 服务发现
                        String serviceName = interfaceClass.getName();
                        String serviceAddress = serviceDiscovery.discover(serviceName);
                        log.info("serviceName: " + serviceName + " serviceAddress: " + serviceAddress);
                        String[] address = serviceAddress.split(":");
                        String host = address[0];
                        int port = Integer.valueOf(address[1]);

                        // 用 Netty 去进行连接这个地址进行交互
                        final RpcProxyHandler rpcProxyHandler = new RpcProxyHandler();
                        EventLoopGroup group = new NioEventLoopGroup();
                        try {
                            Bootstrap bootstrap = new Bootstrap();
                            bootstrap.group(group).channel(NioSocketChannel.class).option(ChannelOption.TCP_NODELAY,true)
                                    .handler(new ChannelInitializer<SocketChannel>() {
                                        @Override
                                        protected void initChannel(SocketChannel socketChannel) throws Exception {
                                            // 业务代码在这里写
                                            ChannelPipeline pipeline = socketChannel.pipeline();
                                            // 如果你用 Netty 进行编程的话，只需要关注 handler即可，NIO BIO 一坨不用管了
                                            pipeline.addLast("frameDecoder",new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,0,4,0,4 ));
                                            pipeline.addLast("frameEncoder",new LengthFieldPrepender(4));
                                            pipeline.addLast("encoder",new ObjectEncoder());
                                            pipeline.addLast("decoder",new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
                                            pipeline.addLast("handler",rpcProxyHandler);
                                        }
                                    });
                            // 连接服务器地址
                            ChannelFuture future = bootstrap.connect(host, port).sync();
                            // 将封装好的 request 对象写过去
                            future.channel().writeAndFlush(rpcRequest);
                            future.channel().closeFuture().sync();
                        }catch (Exception e){
                            e.printStackTrace();
                        } finally {
                            group.shutdownGracefully();
                        }
                        return rpcProxyHandler.getResponse();
                    }
                });
    }
}
